// Copyright (c) [2019] [huazhonghua]
// [理解] is licensed under the Mulan PSL v1.
// You can use this software according to the terms and conditions of the Mulan PSL v1.
// You may obtain a copy of Mulan PSL v1 at:
//     http://license.coscl.org.cn/MulanPSL
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
// PURPOSE.
// See the Mulan PSL v1 for more details.

#ifndef BASIC_H
#define BASIC_H

#include <wchar.h>

#include "structure.h"
#include "labels.h"

Node* get_create_wchar_t(Node* all, wchar_t c) {
	int parts[] = {WCHAR_T, INSTANCE, c};
	return get_create_node(all, all, parts, 3);
}

Node* get_chars_lookup(Node* all, int type, Node* chars) {
	int lookup_path[] = {type, LOOKUP};
	Node* lookup = get_create_node(all, all, lookup_path, 2);

	List* char_list = chars->other.pointer;
	int chars_length = char_list->length;

	int lookup_parts[chars_length];
	for(int i = 0; i < chars_length; ++i) {
		lookup_parts[i] = char_list->nodes[i]->part;
	}
	return get_create_node(all, lookup, lookup_parts, chars_length);
}

Node* get_string_lookup(Node* all, Node* chars) {
	return get_chars_lookup(all, STRING, chars);
}

Node* get_name_lookup(Node* all, Node* chars) {
	return get_chars_lookup(all, NAME, chars);
}

Node* c_string_to_node(Node* all, wchar_t* chars) {
	int length = wcslen(chars);
	Node* node = create_list_node(all, length);
	List* list = node->other.pointer;

	for(int i = 0; i < length; ++i) {
		add_to_list(list, get_create_wchar_t(all, chars[i]));
	}
	return node;
}

Node* get_string(Node* all, Node* chars) {
	Node* lookup_node = get_string_lookup(all, chars);
	return lookup_node->other.pointer;
}

Node* get_create_string_by_node(Node* all, Node* chars, Node* language) {
	List* char_list = chars->other.pointer;
	if(char_list == NULL || char_list->length <= 0) return NULL;

	Node* lookup_node = get_string_lookup(all, chars);
	if(lookup_node->other.pointer != NULL) {
		return lookup_node->other.pointer;
	}

	Node* string = get_create_part_node(all, all, STRING);
	Node* string_instance = create_instance(all, string);

	if(language != NULL) {
		link_node(string_instance, LANGUAGE, language);
	}

	List* string_chars = create_list(char_list->length);
	for(int i = 0; i < char_list->length; ++i) {
		add_to_list(string_chars, char_list->nodes[i]);
	}
	string_instance->other.pointer = string_chars;

	lookup_node->other.pointer = string_instance;
	return string_instance;
}

Node* get_create_string(Node* all, wchar_t* chars, Node* language) {
	Node* chars_node = c_string_to_node(all, chars);
	Node* string = get_create_string_by_node(all, chars_node, language);
	free_list_node(chars_node);
	return string;
}

Node* get_int(Node* all, int i) {
	int parts[] = {INTEGER, INSTANCE, i};
	return get_chained(all, parts, 3);
}

Node* get_create_int(Node* all, int i) {
	int parts[] = {INTEGER, INSTANCE, i};
	Node* node = get_create_node(all, all, parts, 3);
	link_node(node, TYPE, get_linked(all, INTEGER));
	return node;
}

int is_tier_1(Node* node, int part) {
	if(node == NULL) return 0;
	Node* tier = get_linked(node, TIER);
	if(tier == NULL) return 0;
	return tier->part == 1 && node->part == part;
}

int is_instance_of(Node* node, int part) {
	if(node == NULL) return 0;
	Node* type = get_linked(node, TYPE);
	if(type == NULL) return 0;
	return is_tier_1(type, part);
}

int is_operation_type(Node* node, int part) {
	if(node == NULL) return 0;
	Node* type = get_linked(node, OPERATION);
	if(type == NULL) return 0;
	return is_tier_1(type, part);
}

int is_goal_instance(Node* node) {
	Node* type = get_linked(node, TYPE);
	if(type == NULL) return 0;
	return is_tier_1(type, GOAL);
}

wchar_t* list_to_c_string(List* list) {
	wchar_t* chars = malloc((list->length + 1) * sizeof(wchar_t));
	for(int i = 0; i < list->length; ++i) {
		chars[i] = list->nodes[i]->part;
	}
	chars[list->length] = L'\0';
	return chars;
}

wchar_t* to_c_string(Node* string) {
	return list_to_c_string(string->other.pointer);
}

void add_unique(Node* list_node, Node* node) {
	int found = 0;
	int length = get_list_node_length(list_node);
	for(int i = 0; i < length; ++i) {
		if(get_list_node_item(list_node, i) == node) {
			found = 1;
			break;
		}
	}
	if(!found) {
		add_to_list_node(list_node, node);
	}
}

void represent_by_string(Node* all, Node* node, Node* string) {
	Node* represents = get_linked(string, REPRESENT);
	if(represents == NULL) {
		represents = create_list_node(all, 1);
		link_node(string, REPRESENT, represents);
	}

	add_unique(represents, node);
}

void add_string(Node* all, Node* node, Node* string) {
	represent_by_string(all, node, string);

	Node* strings = get_linked(node, STRING);
	if(strings == NULL) {
		strings = create_list_node(all, 1);
		link_node(node, STRING, strings);
	}
	add_unique(strings, string);
}

void add_string_from_c(Node* all, Node* node, wchar_t* chars, Node* language) {
	Node* string = get_create_string(all, chars, language);
	add_string(all, node, string);
}

Node* add_basic(Node* all, int part) {
	Node* node = create_node_only();
	node->part = part;
	link_node(all, part, node);

	Node* one = get_create_int(all, 1);
	link_node(node, TIER, one);
	
	return node;
}

Node* find_by_chars(Node* all, Node* context, Node* chars, Node* type) {
	Node* string = get_string(context, chars);

	if(string == NULL) return NULL;

	Node* represents = get_linked(string, REPRESENT);
	if(type == NULL) return get_list_node_head(represents);

	int length = get_list_node_length(represents);
	for(int i = 0; i < length; ++i) {
		Node* represent = get_list_node_item(represents, i);
		Node* represent_type = get_linked(represent, TYPE);
		if(represent_type == type) return represent;
	}

	return NULL;
}

Node* find_by_c_string(Node* all, Node* context, wchar_t* c_str, Node* type) {
	Node* chars = c_string_to_node(all, c_str);
	Node* node = find_by_chars(all, context, chars, type);
	free_list_node(chars);
	return node;
}

#endif
